// ... 源码 有缩减

//代码注释：https://blog.csdn.net/dongcehao/article/details/106987886


function patchVnode (
  oldVnode,            // 旧节点
  vnode,               // 新节点
  insertedVnodeQueue,  // 插入节点的队列
  ownerArray,          // 节点 数组
  index,               // 当前 节点的
  removeOnly           // 只有在 patch 函数中被传入，当老节点不是真实的 dom 节点，当新老节点是相同节点的时候
) {
  // 如果新节点和旧节点 相等(使用了 同一个地址，直接返回不进行修改)
  // 这里就是 当 props 没有改变的时候，子组件不会做渲染，而是直接复用
  if (oldVnode === vnode) {
    return
  }

  if (isDef(vnode.elm) && isDef(ownerArray)) {
    // clone reused vnode
    vnode = ownerArray[index] = cloneVNode(vnode)
  }

  const elm = vnode.elm = oldVnode.elm

  // 当 当前节点 是 注释节点(被 v-if )了，或者是一个 异步函数节点，那不执行
  if (isTrue(oldVnode.isAsyncPlaceholder)) {
    if (isDef(vnode.asyncFactory.resolved)) {
      hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
    } else {
      vnode.isAsyncPlaceholder = true
    }
    return
  }

  // reuse element for static trees.
  // note we only do this if the vnode is cloned -
  // if the new node is not cloned it means the render functions have been
  // reset by the hot-reload-api and we need to do a proper re-render.

  // 当前节点 是一个静态节点的时候，或者 标记了 once 的时候，那不执行
  if (isTrue(vnode.isStatic) &&
    isTrue(oldVnode.isStatic) &&
    vnode.key === oldVnode.key &&
    (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
  ) {
    vnode.componentInstance = oldVnode.componentInstance
    return
  }

  let i
  const data = vnode.data
  // 调用 prepatch 的钩子函数
  if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
    i(oldVnode, vnode)
  }

  const oldCh = oldVnode.children
  const ch = vnode.children
  // 调用 update 钩子函数
  if (isDef(data) && isPatchable(vnode)) {
    // 这里 的 update 钩子函数式 vnode 本身的钩子函数
    for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
     // 这里的 update 钩子函数  是 用户传过来的 钩子函数
    if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
  }

  // 新节点 没有 text 属性
  if (isUndef(vnode.text)) {
    // 如果 当前节点  都有子节点，对比更新子节点
    if (isDef(oldCh) && isDef(ch)) {
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
    } else if (isDef(ch)) { // 新节点存在，但是老节点不存在
      if (process.env.NODE_ENV !== 'production') {
        checkDuplicateKeys(ch)
      }
      // 如果老节点是  text， 清空
      if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
      // 增加子节点
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
    } else if (isDef(oldCh)) {
      // 老节点存在，但是新节点不存在，执行删除
      removeVnodes(oldCh, 0, oldCh.length - 1)
    } else if (isDef(oldVnode.text)) {
      // 如果老节点是  text， 清空
      nodeOps.setTextContent(elm, '')
    }
  } else if (oldVnode.text !== vnode.text) {
    // 新旧节点 text 属性不一样
    // 将 text 设置为 新节点的 text
    nodeOps.setTextContent(elm, vnode.text)
  }
  if (isDef(data)) {
    // 执行 postpatch 钩子函数
    if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
  }
}

// ...